package hardBD;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.LinkedList;
import java.util.Queue;


public class BeatDetector {
	byte[] a;
	byte[] b;
	byte[] FFTBuffer;
	double[] converted;
	int i = 1;
	FFT fft = new FFT(1024);
	
	public BeatDetector(String filePath){
		AudioLoaderV2 loader = new AudioLoaderV2();
		//byte[] track = loader.load(filePath);
		byte[] track = loader.load60();
		a = new byte[1024];
		b = new byte[1024];		
		FFTBuffer = new byte[1024]; 
		
		/* Do we really need bytes?
		 * try double / float arrays instead
		 */
		
		double[] zeroes = new double[1024];
		Arrays.fill(zeroes, 0);
		
		//Arbitrary?
		for(int i = 0; i < 1024; i++){
			FFTBuffer[i] = (byte)fftComplex(a[i], i, b[i]);
		}
		
		
		byte[] test = Arrays.copyOfRange(track, 1024, 2048);		
		
		
		
		ArrayList<int[]> hist = new ArrayList<int[]>();
		
		//Subband Energy History
		//byte[][] subEHist = new byte[43][64];
		Queue<Double>[] subEHistory = new LinkedList[64];
		for(int i = 0; i < 64; i++){
			subEHistory[i] = new LinkedList();
		}
		
		final int queueLength = 43;
		byte[] newVals = new byte[64];
		int startingPoint = 0; //point in the byte array we start from
		final int increment = 1024; //sub size
		final int secondsToTest = 5; //number of seconds we try to deduce the beat from
		
		for(int i = startingPoint; i < startingPoint + (increment * 43 * secondsToTest); i += increment){
			//Dummy array
			test = Arrays.copyOfRange(track, i, i + increment);
			//System.out.println("1");
			converted = new double[1024];
			FFTBuffer = test;
			//System.out.println("2");
			for(int j = 0; j < converted.length; j++){
				converted[j] = FFTBuffer[j];
			}
			//System.out.println("3");
			fft.fft(converted, zeroes);
			//ArrayList<byte[]> freqDomain = splitFFT();
			//converted should now be the frequency domain
			for(int j = 0; j < converted.length; j++){
				converted[j] = FFTBuffer[j];
			}
			ArrayList<double[]> splitFFTs = splitFFT();
			for(double[] jj : splitFFTs){
				//System.out.println(Arrays.toString(jj));
			}
			//System.out.println("4");
			double[] energies = calculateEnergies(splitFFTs);
			for(int x = 0; x < energies.length; x++){
				//System.out.println(energies[x]);
			}
			//System.out.println("5");
			if(subEHistory[63].size() < queueLength){
				for(int j = 0; j < energies.length; j++){
					subEHistory[j].offer(energies[j]);
					//System.out.println("adding: " + energies[j]);
				}
			}else{
				for(int j = 0; j < energies.length; j++){
					subEHistory[j].poll();
					subEHistory[j].offer(energies[j]);
				}
			}
			
			
			
			
		}		
		double[] ttt = new double[64];
		int ctt = 0;
		for(Queue qt2 : subEHistory){
			NewQueue nq = new NewQueue(qt2);

			//calculateVariance(nq.queue);
			ttt[ctt] = calculateAverageEnergy(nq.queue);
			ctt++;
		}
		//System.out.println(Arrays.toString(ttt));
		//printQueues(subEHistory);
	}
	
	public void printQueues(Queue[] t){
		for(Queue x : t){
			Object[] obj = x.toArray();
			String res = "";
			for(int i = 0; i < obj.length; i++){
				res += obj[i].toString() + ", ";
			}
			System.out.println(res);
		}
	}
	
	public double fftComplex(byte a, double i, byte b){
		return a + i * b;
	}
		
	public double calculateVariance(Queue<Double> energyHistory){
		double average = calculateAverageEnergy(energyHistory);
		double sum = 0;
		for(int i = 0; i < 43; i++){
			sum += Math.pow((energyHistory.poll()-average), 2);
		}
		System.out.println(sum);
		if (sum >= 150){			
			return sum;
		}else{
			return 0.0;
		}
	}
	
	public double calculateAverageEnergy(Queue<Double> energyHistory){
		double sum = 0;
		for(int i = 0; i < 40; i++){
			//System.out.println("Doing: " + sum + " + " + energyHistory.peek());
			sum += (double)energyHistory.poll();
		}
		//System.out.println("sum  " + sum);
		return sum/43.0;
	}
	
	public double[] calculateEnergies(ArrayList<double[]> freqDomain){		
		//returns energy on 64 subbands
		double[] energies = new double[64];
		//System.out.println("4.1");
		//ref equation http://archive.gamedev.net/archive/reference/programming/features/beatdetection/forumla7-2.png
		for(int i = 0; i < 64; i++){
			//Double check loop counters "haha matlab"
			//System.out.println("4.2");
			double[] B = freqDomain.get(i);
					
			double sum = 0; int k = 0; int runs = 0;
			//System.out.println("4.3");	
			//Calculate Bottom
			for(int j = 0; j < i - 1; j++){
				k += calculateWidth(j);
			}
			//System.out.println("4.4");
			//Calculate Top
			for(int j = 0; j < i; j++){
				runs += calculateWidth(j);
			}
			//System.out.println("4.5");
			//piece loop together
			//System.out.println(runs);
			int count = 0;
			for(int y = k; y < runs; y++){
				//System.out.println(i);
				sum += B[count];
				count++;
			}
			//System.out.println("4.6");
			//calculate energy
			energies[i] = (calculateWidth(i)/1024.0) * sum;
			//System.out.println(calculateWidth(i)/1024.0);
		}		
		return energies;
	}
	
	public int calculateWidth(int i){
		double i1 = i;
		double w = ((28.0/63.0) * i1 + 98.0/63.0);
		int wi = (int) Math.ceil(w);
		return wi;
	}
	
	
	public ArrayList<double[]> splitFFT(){
		ArrayList<double[]> result = new ArrayList<double[]>();		
		int ct = 0;
		for(int i = 0; i < 64; i++){
			int wi  = calculateWidth(i);
			double[] temp = new double[wi];
			for(int j = 0; j < wi; j++){
				temp[j] = converted[ct]; //not sure which array, double check
				ct++;
			}
			result.add(temp);
		}
		
		
		return result;
	}
	
	/*public double[] calculateEnergies(ArrayList<byte[]> freqDomain){		
		//returns energy on 64 subbands
		double[] energies = new double[64];
				
		//ref equation http://archive.gamedev.net/archive/reference/programming/features/beatdetection/forumla7-2.png
		for(int i = 0; i < 64; i++){
			//Double check loop counters "haha matlab"
						
			byte[] B = freqDomain.get(i);
					
			int sum = 0; int k = 0; int runs = 0;
						
			//Calculate Bottom
			for(int j = 0; j < i - 1; j++){
				k += calculateWidth(j);
			}
			
			//Calculate Top
			for(int j = 0; j < i; j++){
				runs += calculateWidth(j);
			}
			
			//piece loop together
			for(int y = k; y < runs; k++){
				sum += B[y];
			}
			
			//calculate energy
			energies[i] = (calculateWidth(i)/1024) * sum;
		}		
		return energies;
	}*/
	
	/*
	public ArrayList<byte[]> splitFFT(){
		ArrayList<byte[]> result = new ArrayList<byte[]>();		
		int ct = 0;
		for(int i = 0; i < 64; i++){
			int wi  = calculateWidth(i);
			byte[] temp = new byte[wi];
			for(int j = 0; j < wi; j++){
				temp[j] = converted[ct]; //not sure which array, double check
				ct++;
			}
			result.add(temp);
		}
		
		
		return result;
	}*/
}
